package com.heima.schedule.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.heima.common.cache.CacheService;
import com.heima.model.schedule.dto.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import com.heima.schedule.service.TaskService;
import com.heima.schedule.service.TaskinfoLogsService;
import com.heima.schedule.service.TaskinfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class TaskServiceImpl implements TaskService {
    @Autowired
    private TaskinfoService taskinfoService;
    @Autowired
    private TaskinfoLogsService taskinfoLogsService;
    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Long addTask(Task task) {
        //0.判断入参是否为空
        if (task==null) {
            log.warn("入参不能为空");
            return null;
        }

        //1.插入taskinfo表
        long executeTime = task.getExecuteTime();
        Taskinfo taskinfo = new Taskinfo();
        BeanUtils.copyProperties(task,taskinfo);
        taskinfo.setExecuteTime(new Date(executeTime));
        boolean tastResult = taskinfoService.save(taskinfo);
        if(!tastResult){
            log.warn("日志插入失败");
            return null;
        }

        //2.插入taskinfo_log表
        TaskinfoLogs taskinfoLogs = new TaskinfoLogs();
        BeanUtils.copyProperties(taskinfo,taskinfoLogs);
        taskinfoLogs.setStatus(0);//默认:0初始化
        boolean logResult = taskinfoLogsService.save(taskinfoLogs);
        if (!logResult) {
            log.warn("任务日志插入失败");
            throw new RuntimeException("任务日志插入失败");
        }
        //3.判断任务执行时间是否小于当前时间
        long currentTime = System.currentTimeMillis();
        if(executeTime<=currentTime){
            cacheService.lLeftPush("TOPIC", JSON.toJSONString(task));
            return taskinfo.getTaskId();
        }
        //4.判断任务执行时间是否小于未来五分钟
        long futureTime = currentTime + (5 * 60 * 1000);
        if(executeTime<=futureTime){
            cacheService.zAdd("FUTURE",JSON.toJSONString(task),executeTime);
        }
        //5.返回任务id
        return taskinfo.getTaskId();
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean cancelTask(Long taskId) {
        //0.参数判空
        if(taskId==null){
            log.warn("参数为空");
            return false;
        }
        //1.删除taskInfo
        boolean taskResult = taskinfoService.removeById(taskId);
        if(!taskResult){
            log.warn("taskInfo删除失败");
            return false;
        }
        //2.更新taskInfoLog(status=2[已取消])
        TaskinfoLogs logs = taskinfoLogsService.getById(taskId);
        logs.setStatus(2);
        boolean logsResult = taskinfoLogsService.updateById(logs);
        if(!logsResult){
            log.warn("taskInfoLog更新失败");
            throw new RuntimeException("taskInfoLog更新失败");
        }
        //3.组装task对象
        Task task = new Task();
        BeanUtils.copyProperties(logs,task);
        Date executeTime = logs.getExecuteTime();
        task.setExecuteTime(executeTime.getTime());
        //4.删除
        String taskJson = JSON.toJSONString(task);
        cacheService.lRemove("TOPIC",taskId,taskJson);
        cacheService.zRemove("FUTURE",taskId,taskJson);
        return true;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public boolean pollTask() {
        //1.指定消费队列(TOPIC)
        String taskJson = cacheService.lRightPop("TOPIC");
        //2.josn转task对象
        Task task = JSON.parseObject(taskJson, Task.class);
        //3.更新
        boolean taskResult = taskinfoService.removeById(task.getTaskId());
        if (!taskResult) {
            log.warn("消费失败");
            return false;
        }
        long executeTime = task.getExecuteTime();
        TaskinfoLogs logs = new TaskinfoLogs();
        BeanUtils.copyProperties(task,logs);
        logs.setExecuteTime(new Date(executeTime));
        boolean logsResult = taskinfoLogsService.updateById(logs);
        if (!logsResult) {
            log.warn("消费失败");
            throw new RuntimeException("消费失败");
        }
        return true;
    }

    @Scheduled(fixedRate = (5*60*1000))
    public void refresh(){
        log.info("我被执行了");
        Set<String> future = cacheService.zRangeByScore("FUTURE", 0, System.currentTimeMillis());
        if (CollectionUtils.isEmpty(future)) {
            log.warn("没有可移动的任务");
            return;
        }
        cacheService.refreshWithPipeline("FUTURE","TOPIC",future);
    }

    @Scheduled(fixedRate = (5*60*1000))
    public void initDate(){
        //1.清除数据
        clear();
        //2.读取mysql里的任务
        List<Taskinfo> list = taskinfoService.list();
        if (CollectionUtils.isEmpty(list)) {
            log.warn("任务为空");
            return;
        }
        for (Taskinfo taskinfo : list) {
            if(taskinfo==null){
                continue;
            }
            long executeTime = taskinfo.getExecuteTime().getTime();
            long currentTime = System.currentTimeMillis();
            long futureTime =currentTime+(5*60*1000);

            Task task = new Task();
            BeanUtils.copyProperties(taskinfo,task);
            task.setExecuteTime(executeTime);
            //3.判断
            if(executeTime<=currentTime){
                cacheService.lLeftPush("TOPIC",JSON.toJSONString(task));
            }else if(executeTime<=futureTime){
                cacheService.zAdd("FUTURE",JSON.toJSONString(task),executeTime);
            }
        }

    }
    private void clear(){
        cacheService.delete("TOPIC");
        cacheService.delete("FUTURE");
    }
}
